#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/input.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/wait.h>

 
// BMP格式头规范
struct bitmap_header
{
	int16_t type;
	int32_t size; // 图像文件大小
	int16_t reserved1;
	int16_t reserved2;
	int32_t offbits; // bmp图像数据偏移量
}__attribute__((packed));
//_attribute__ ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐
struct bitmap_info
{
	int32_t size;   // 本结构大小	
	int32_t width;  // 图像宽
	int32_t height; // 图像高
	int16_t planes;
 
	int16_t bit_count; // 色深
	int32_t compression;
	int32_t size_img; // bmp数据大小，必须是4的整数倍
	int32_t X_pel;
	int32_t Y_pel;
	int32_t clrused;
	int32_t clrImportant;
}__attribute__((packed));
 
// 以下结构体不一定存在于BMP文件中，除非：
// bitmap_info.compression为真
struct rgb_quad
{
	int8_t blue;
	int8_t green;
	int8_t red;
	int8_t reserved;
}__attribute__((packed));

//触摸屏文件
#define TS_PATH  "/dev/input/event0"
//屏幕文件
#define FB_FILE  "/dev/fb0"

//触摸屏文件
int ts_fd;
//屏幕文件
int lcd_fd;

//映射文件指针
unsigned int *mem_p;
//坐标点
int X , Y ;


int lcd_init(void);
int lcd_uninit(void);
int show_bmp(const char *pathname);
int get_xy(void);
void show_one(void);
int show_bmp_xy(const char *pathname, int start_x, int start_y);


int main(void)
{
    int n = 0; // 状态变量，用于控制程序流程
     int continueDisplay = 1; // 新增标志变量，用于控制循环
    
    while (1) 
    {
        // 根据状态变量n来决定执行的流程
        switch (n) 
        {
            case 0:
                // 显示主页面
                show_one();
                while (continueDisplay)
                {
                    show_bmp_xy("wfl.bmp",15,150);
                    sleep(1);
                    show_bmp_xy("mlh.bmp",15,150);
                    sleep(1);
                    

                    get_xy(); // 获取触摸坐标
                    // 检测是否触摸了管理员登录区域
                    if (Y < 100 && X > 650) 
                    {
                        continueDisplay = 0; // 设置标志变量，退出循环
                    }
                    
                }
                n = 1; // 状态变更为1
                break;
            case 1:
                // get_xy(); // 获取触摸坐标
                // 检测是否触摸了管理员登录区域
                lcd_init();
                show_bmp("password.bmp");
                lcd_uninit();
                n = 2; // 状态变更为2
                break;
            case 2:
                get_xy(); // 再次获取触摸坐标
                // 检测是否触摸了返回按钮
                if (Y < 100 && X < 100) 
                {
                    n = 0; // 返回主页面
                    continueDisplay = 1;
                    break;
                }
                if ( X < 250 && Y > 410) 
                {
                    n = 3;
                    break;
                }
                break;
            case 3:
                get_xy(); // 再次获取触摸坐标
                lcd_init();
                show_bmp("user.bmp");
                lcd_uninit();
                // 检测是否触摸了返回按钮
                if (X > 200 && X < 600 && Y > 410 && Y < 460) 
                {
                    n = 0; // 返回主页面
                    continueDisplay = 1;
                    break;
                }
                break;
        }
    }
}

void show_one()
{
    //主页面
    lcd_init();
    show_bmp("theme.bmp");
}

/** 
 * 初始化屏幕文件
 * 
**/
int lcd_init(void)
{
    //打开frame buffer设备文件--屏幕文件
    lcd_fd = open(FB_FILE, O_RDWR);
    if(lcd_fd == -1)
    {
        perror("open error:");
        return -1;
    }

    //屏幕映射
    mem_p = (unsigned int *)mmap(NULL, 800*480*4, PROT_READ|PROT_WRITE, MAP_SHARED, lcd_fd, 0);
    if(mem_p == MAP_FAILED)
    {
        perror("mmap");
        return -1;
    }

    return 0;
}

/** 
 * 申请内存映射内存
 * 
**/
int lcd_uninit(void)
{
   // 解除映射
    if (munmap(mem_p, 800*480*4) == -1) 
    {
        perror("munmap error:");
        return -1;
    }
    close(lcd_fd);
    return 0;
}

/** 
 * 显示一张800×480的24位bmp图片，并将图片放入内存映射空间
 * 
**/
int show_bmp(const char *pathname)
{
    int bmp_fd, i,j;
    //存储bmp文件的像素点
    unsigned char bmp_buff[800*480*3] = {0};
    //写进内存映射缓冲区的缓冲区
    unsigned int *buff = malloc(800*480*sizeof(unsigned int)); // 使用动态分配的内存

    if (buff == NULL) 
    {
        perror("malloc error:");
        return -1;
    }

    bmp_fd = open(pathname, O_RDONLY);
    if(bmp_fd == -1)
    {
        perror("open");
        free(buff);
        return -1;
    }

    //跳过54个字节头
    lseek(bmp_fd, 54, SEEK_SET);

    //读图片的全部RGB到缓冲区
    if (read(bmp_fd, bmp_buff, sizeof(bmp_buff)) != sizeof(bmp_buff)) 
    {
        perror("read error:");
        free(buff);
        close(bmp_fd);
        return -1;
    }

    //将RGB转换为BGR
    for(i=0; i<800*480; i++)
    {
        buff[i] = (bmp_buff[3*i+2] << 16) | (bmp_buff[3*i+1] << 8) | bmp_buff[3*i+0];//AGRB
    }

    // 像素点填充到屏幕
    for(i = 0; i < 480; i++) // 按行遍历
    { 
        for(j = 0; j < 800; j++) // 按列遍历
        { 
            // 从buff中获取像素数据，确保使用正确的索引
            unsigned int pixel = buff[i * 800 + j]; //i=0 :0 - 799
            
            // 将像素数据复制到帧缓冲区mem_p中
            // 确保使用正确的索引，这里假设mem_p的布局与buff相同
            mem_p[((479 - i) * 800) + j] = pixel;  //479*800 - 479*800+799
        }
    }

    free(buff);
    close(bmp_fd);
    return 0;
}

/** 
 * 获取X Y坐标值
 * 
**/

int get_xy(void)
{
    struct input_event ts;

    ts_fd = open(TS_PATH, O_RDWR);
    if(ts_fd == -1)
    {
        printf("open ts failure\n");
        return -1;
    }

    read(ts_fd, &ts, sizeof(struct input_event));

            

    //判断类型
    if(ts.type == EV_ABS && ts.code == ABS_X)
    {
        X = ts.value;
    }

    if(ts.type == EV_ABS && ts.code == ABS_Y)
    {
        Y = ts.value;
    }



            

        //判断按下
        if(ts.type == EV_KEY && ts.code ==  BTN_TOUCH && ts.value == 1)
        {

        }

        //判断是否松开
        if(ts.type == EV_KEY && ts.code ==  BTN_TOUCH && ts.value == 0)
        {                                                  	             	
            X = X*(800.0/1024.0);      
            Y = Y*(480.0/600.0);
        }           

        printf("X:%d, Y:%d\n", X, Y);

}
/**
 * 在指定位置显示图片
 */

int show_bmp_xy(const char *pathname, int start_x, int start_y)
{
    int bmp_fd, x, y, i, j;
 
	// 读取BMP格式头，获取图片信息
	struct bitmap_header header;
	struct bitmap_info info;
	struct rgb_quad quad;
 
    bmp_fd = open(pathname, O_RDWR);
    if(bmp_fd == -1)
    {
        printf("open bmp fail\n");
        return -1;
    }   
 
 
    // 第一、二个结构体是必有信息
	read(bmp_fd, &header, sizeof(header));
	read(bmp_fd, &info, sizeof(info));
 
 
    
    // 第三个结构体是可选的，需要判断是否存在
	if(info.compression != 0)
	{
		read(bmp_fd, &quad, sizeof(quad));
		fprintf(stderr, "read quad! \n");
	}
 
    // 输出图片相关信息
    printf("图片大小: %d\n", header.size);
    printf("图片尺寸: %d×%d\n", info.width, info.height);
    printf("色深: %dbpp\n", info.bit_count);
 
    int width = info.width;
    int height = info.height;
 
 
    if((start_x+width) > 800  || (start_y+height) > 480)
    {
        printf("图片越界,无法显示\n");
        close(bmp_fd);
        return -1;
    }
 
 
    //定义存储图片所有RGB数据的缓冲区
    unsigned char *bmp_buff = (unsigned char *)malloc(width*height*3);
    unsigned int *buff = (unsigned int *)malloc(width*height*4);
 
    //将图片的像素存放bmp_buf当
    read(bmp_fd, bmp_buff, width*height*3);
 
    for(i=0; i<width*height; i++)
    {
        buff[i] = bmp_buff[3*i+0]|bmp_buff[3*i+1]<<8|bmp_buff[3*i+2]<<16;
    }   
 
    //像素点填充到屏幕
    for(y=0; y<height; y++)
    {
        for(x=0; x<width; x++) 
        {
            mem_p[(start_y+y)*800+start_x+x] = buff[(height-1-y)*width+x];
        }
    }
 
 
    free(bmp_buff);
 
    free(buff);
    close(bmp_fd);
 
}